package com.balloon.gateway.security;

import com.balloon.gateway.converter.JsonBodyAuthConverter;
import com.balloon.gateway.security.handler.*;
import com.balloon.gateway.security.manager.SmsAuthenticationManager;
import com.balloon.gateway.security.manager.TokenAuthenticationManager;
import com.balloon.gateway.security.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.DelegatingReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsConfigurationSource;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.LinkedList;

/**
 * @author liaofuxing
 * @version 1.0.0
 * @date 2022/3/8 11:12
 * @description webflux security核心配置类
 */
@EnableWebFluxSecurity
public class WebFluxSecurityConfig {

    @Autowired
    private UserDetailsServiceImpl userDetailsServiceImpl;

    @Autowired
    private TokenLogoutSuccessHandler tokenLogoutSuccessHandler;

    @Resource
    private DefaultAuthenticationSuccessHandler defaultAuthenticationSuccessHandler;

    @Resource
    private DefaultAuthenticationFailureHandler defaultAuthenticationFailureHandler;

    @Resource
    private TokenAuthenticationManager tokenAuthenticationManager;

    @Resource
    private SmsAuthenticationManager smsAuthenticationManager;


    @Resource
    private DefaultSecurityContextRepository defaultSecurityContextRepository;

    @Resource
    private DefaultAuthenticationEntryPoint defaultAuthenticationEntryPoint;

    @Resource
    private DefaultAccessDeniedHandler defaultAccessDeniedHandler;

    @Resource
    private JsonBodyAuthConverter jsonBodyAuthConverter;

//    /**
//    * 自定义过滤权限
//    */
//    @Value("${security.noFilter}")
//    private String noFilter;


    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity httpSecurity) {
        //认证过滤器

        httpSecurity
                .securityContextRepository(defaultSecurityContextRepository)
                // 请求拦截处理
                .authorizeExchange()
                .pathMatchers("/sendSms","/*/onlinePreview","/*/parDownload").permitAll()
                .pathMatchers(HttpMethod.OPTIONS).permitAll()
                .anyExchange().authenticated()
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(defaultAuthenticationEntryPoint)
                .and()
                .exceptionHandling()
                .accessDeniedHandler(defaultAccessDeniedHandler)
                .and()
                .logout().logoutUrl("/logout")
                // 设置登出成功处理器（下面介绍）
                .logoutSuccessHandler(tokenLogoutSuccessHandler)
                .and()
                .httpBasic().disable()
                .formLogin().disable()
                .cors().configurationSource(corsConfigurationSource())
                .and()
                .csrf().disable();
        httpSecurity.addFilterAt(authenticationWebFilter(), SecurityWebFiltersOrder.AUTHENTICATION);
//        httpSecurity.addFilterBefore(authenticationWebFilter(), SecurityWebFiltersOrder.AUTHENTICATION);
        return httpSecurity.build();
    }

    CorsConfigurationSource corsConfigurationSource() {
        // 提供CorsConfiguration实例，并配置跨域信息
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowedHeaders(Collections.singletonList("*"));
        corsConfiguration.setAllowedOriginPatterns(Collections.singletonList("*"));
        corsConfiguration.setAllowedMethods(Collections.singletonList("*"));
        corsConfiguration.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfiguration);
        return  source;
    }


    //密码加密器，在授权时，框架为我们解析用户名密码时，密码会通过加密器加密在进行比较
    //将密码加密器交给spring管理，在注册时，密码也是需要加密的，再存入数据库中
//    //用户输入登录的密码用加密器加密，再与数据库中查询到的用户密码比较
//    @Bean
//    public BCryptPasswordEncoder bCryptPasswordEncoder() {
//        return new BCryptPasswordEncoder();
//    }

    /**
     * BCrypt密码编码
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    /**
     * 注册用户信息验证管理器，可按需求添加多个按顺序执行
     * @author liaofuxing
     */
    @Bean
    ReactiveAuthenticationManager reactiveAuthenticationManager() {
        LinkedList<ReactiveAuthenticationManager> managers = new LinkedList<>();
//         managers.add(authentication -> {
////             Mono<UserDetails> byUsername = userSmsDetailsServiceImpl.findByUsername(authentication.getPrincipal().toString(), authentication.getCredentials().toString());
//              return Mono.empty();
//         });
        // 必须放最后不然会优先使用用户名密码校验但是用户名密码不对时此 AuthenticationManager 会调用 Mono.error 造成后面的 AuthenticationManager 不生效
        managers.add(smsAuthenticationManager);
        managers.add(new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsServiceImpl));
        managers.add(tokenAuthenticationManager);
        return new DelegatingReactiveAuthenticationManager(managers);
    }

    /**
     * 身份认证
     * @return
     */
     @Bean
     AuthenticationWebFilter authenticationWebFilter() {
         AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(reactiveAuthenticationManager());
         authenticationWebFilter.setRequiresAuthenticationMatcher(ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, "/login"));
//         authenticationWebFilter.setServerAuthenticationConverter(new JsonBodyAuthConverter());
         authenticationWebFilter.setServerAuthenticationConverter(jsonBodyAuthConverter);
//         authenticationWebFilter.setSecurityContextRepository(defaultSecurityContextRepository);
//         authenticationWebFilter.setAuthenticationConverter(jsonBodyAuthenticationConverter());
         authenticationWebFilter.setAuthenticationSuccessHandler(defaultAuthenticationSuccessHandler);
         authenticationWebFilter.setAuthenticationFailureHandler(defaultAuthenticationFailureHandler);
         return authenticationWebFilter;
     }

}